const path = require("path");
const { readFileSync, writeFileSync, renameSync } = require("fs");
const { values, positionals } = require("util").parseArgs({
	args: process.argv.slice(2),
	options: {
		profile: {
			type: "string"
		}
	},
	strict: true,
	allowPositionals: true
});

const { spawn, spawnSync } = require("child_process");

const NAPI_BINDING_DTS = "napi-binding.d.ts"
const CARGO_SAFELY_EXIT_CODE = 0;

const watch = process.argv.includes("--watch");

build().then((value) => {
	// Regarding cargo's non-zero exit code as an error.
	if (value !== CARGO_SAFELY_EXIT_CODE) {
		process.exit(value);
	}
}).catch(err => {
	console.error(err);
	process.exit(1);
});

async function build() {
	return new Promise((resolve, reject) => {
		const args = [
			"build",
			"--platform",
			"--dts",
			NAPI_BINDING_DTS,
			"--no-js",
			// "--no-const-enum",
			"--no-dts-header",
			"--pipe",
			`"node ${path.resolve(__dirname, "dts-header.js")}"`
		];
		const rustflags = []
		const features = [];
		const envs = { ...process.env };
		const use_build_std = values.profile === "release"
				|| values.profile === "release-debug"
				|| values.profile === "release-wasi"
				|| values.profile === "profiling";

		if (values.profile) {
			args.push("--profile", values.profile);
		}
		if (watch) {
			args.push("--watch");
		}
		if (process.env.USE_ZIG) {
			args.push("--cross-compile");
		}
		if (process.env.RUST_TARGET) {
			args.push("--target", process.env.RUST_TARGET);
		}
		if (!process.env.DISABLE_PLUGIN) {
			args.push("--no-default-features");
			features.push("plugin");
		}
		if (process.env.RSPACK_TARGET_BROWSER) {
			features.push("browser")
			// Strip debug format to reduce wasm size of @rspack/browser
			rustflags.push("-Zfmt-debug=none");
		}
		args.push("--no-dts-cache");
		if (!values.profile || values.profile === "dev") {
			features.push("color-backtrace");
		}
		if (process.env.SFTRACE) {
			features.push("sftrace-setup");
			rustflags.push("-Zinstrument-xray=always");
		}
		if (process.env.ALLOCATIVE) {
			features.push("allocative");
			rustflags.push("--cfg=allocative");
		}
		if (process.env.TRACY) {
			features.push("tracy-client");
		}
		if (values.profile === "release") {
			features.push("info-level");
			if (process.env.RUST_TARGET && !process.env.RUST_TARGET.includes("windows-msvc")) {
				rustflags.push("-Cforce-unwind-tables=no");
			}
		}
		if (features.length) {
			args.push("--features " + features.join(","));
		}

		if (positionals.length > 0 || rustflags.length > 0 || use_build_std) {
			// napi need `--` to separate options and positional arguments.
			args.push("--");

			if (rustflags.length > 0) {
				const flag = rustflags.map(f => `\\"${f}\\"`).join(",");
				args.push("--config");
				args.push(`"target.'cfg(all())'.rustflags = [${flag}]"`)
			}

			if (use_build_std) {
				// allows to optimize std with current compile arguments
				// and avoids std code generate unwind table to save size.
				args.push("-Zbuild-std=panic_abort,std");
			}

			if (positionals.length > 0) {
				args.push(...positionals);
			}
		}

		console.log(`Run command: napi ${args.join(" ")}`);

		const cp = spawn("napi", args, {
			stdio: "inherit",
			shell: true,
			env: envs,
		});

		cp.on("error", reject);
		cp.on("exit", (code) => {
			if (code === CARGO_SAFELY_EXIT_CODE) {

				// Fix an issue where napi cli does not generate `string_enum` with `enum`s.
				const dts = path.resolve(__dirname, "..", NAPI_BINDING_DTS);
				writeFileSync(dts,
					readFileSync(dts, "utf8")
						.replaceAll("const enum", "enum")
						// Remove the NormalModule type declaration generated by N-API.
						// We manually declare the NormalModule type in banner.d.ts
						// This allows users to extend NormalModule with static methods through type augmentation.
						.replaceAll(/export\s+declare\s+class\s+NormalModule\s*\{([\s\S]*?)\}\s*(?=\n\s*(?:export|declare|class|$))/g, "")
				);

				// For browser wasm, we rename the artifacts to distinguish them from node wasm
				if (process.env.RSPACK_TARGET_BROWSER) {
					renameSync("rspack.wasm32-wasi.debug.wasm", "rspack.browser.debug.wasm")
					renameSync("rspack.wasm32-wasi.wasm", "rspack.browser.wasm")
				}

				if(process.env.TRACY){
					// split debug symbols for tracy
				  spawnSync('dsymutil', [
						path.resolve(__dirname, "..", "rspack.darwin-arm64.node")
					], {
						stdio: "inherit",
						shell: true,
					})
				}
			}
			resolve(code);
		});
	});
}
